#ifndef __HTTP__MUDUO__SERVER
#define __HTTP__MUDUO__SERVER

#include"../server.hpp" 
#include <cstdio>
#include <fstream>
#include<regex>
#include<ctype.h>


class Util
{
public:
//字符串分割
    static int Split(const std::string& src ,std::string sep , std::vector<std::string>* ret)  //返回子串的数量
    {
        int offset = 0;     //每一轮 find 的起始位置
        while(offset < src.size())  //当分割字符串为结尾时不会进循环
        {
            size_t pos = src.find(sep,offset);
            if(pos == std::string::npos) //说明找完了，这是最后一个子串
            {
                ret->push_back(src.substr(offset));     //直接截取到结尾 
                offset = pos + sep.size();
                break;
            }
            //说明还没有找到结尾
            if(pos == 0 ) //说明开头是分割字符串，我们忽略
            {
                offset = pos+sep.size();
                continue;
            }
            //否则就是正常的不涉及边界匹配
            ret->push_back(src.substr(offset,pos - offset));     // 截取的长度是pos - offset   [offset,pos)
            offset = pos+sep.size();
        }
        return ret->size();
    }
//读取文件    
    static bool ReadFile(const std::string& filename , std::string* out)
    {
        std::ifstream ifs(filename,std::ios::binary);
        if(ifs.is_open() == false) //打开文件失败
        {
            DEBUG_LOG("open file failed");
            return false;
        }
        ifs.seekg(0,ifs.end);
        size_t fsize = ifs.tellg();
        ifs.seekg(0,ifs.beg);
        out->resize(fsize);
        ifs.read(&((*out)[0]),fsize);
        if(ifs.good() == false)  //read 失败
        {
            DEBUG_LOG("ifstread read failed");
            ifs.close();    //注意要关闭文件
            return false;
        }
        ifs.close();
        return true;
    }
//写入文件
    static bool WriteFile(const std::string& filename , const std::string& in)
    {
        std::ofstream ofs(filename,std::ios::trunc);
        if(ofs.is_open() == false)
        {
            DEBUG_LOG("open file failed");
            return false;
        }
        ofs.write(in.c_str(),in.size());
        if(ofs.good() == false)
        {
            DEBUG_LOG("ofstream write failed");
            ofs.close();
            return false;
        }
        ofs.close();
        return true;
    }
//URL编码
    static std::string UrlEncode(const std::string& in , bool flag)  //true 表示需要将空格转换为 + ，false表示按照正常的转换规则
    {
        std::string ret;
        for(auto ch:in)
        {
            if(ch == '.' | ch == '-' | ch == '_' | ch == '~')
            {
                ret += ch;
                continue;
            }
            if(isalpha(ch) | isdigit(ch))  //字母或数字
            {
                ret += ch;
                continue;
            }
            //走到这里就是特殊字符
            if(ch == ' ' && flag)  //特殊转换
            {
                ret += '+';
                continue;
            }
            //正常转换规则
            char tmp[4] ={0};
            snprintf(tmp,4,"%%%02x",ch);
            ret += tmp;
        }
        return ret;
    }
//十六进制字符转十进制整数
    static int hextoi(char ch)
    {
        if(ch >='0' && ch <='9') return ch-'0';
        if(ch >= 'a' && ch <= 'f') return ch-'a'+10;
        ERROR_LOG("hextoi failed"); 
        return -1;
    }
//URL解码
    static std::string UrlDecode(const std::string& src , bool flag) //true表示将 + 解码为空格
    {
        std::string ret;
        for(int i =0;i<src.size();++i)
        {
            if(src[i] == '+' && flag)
            {
                ret += ' ';
                continue;
            }
            if(src[i] == '%')
            {
                int n1 = hextoi(src[i+1]);  //将十六进制字符转换为十进制
                int n2 = hextoi(src[i+2]);
                int ch = n1<<4 + n2;
                i+=2;
                ret += ch;
                continue;
            }
            ret += src[i];
        }
        return ret;
    }
//状态码描述
    static std::string StatuDesc(int statu)
    {
        static std::unordered_map<int,std::string> _status_desc{
            {100, "Continue"},
            {101, "Switching Protocol"},
            {102, "Processing"},
            {103, "Early Hints"},
            {200, "OK"},
            {201, "Created"},
            {202, "Accepted"},
            {203, "Non-Authoritative Information"},
            {204, "No Content"},
            {205, "Reset Content"},
            {206, "Partial Content"},
            {207, "Multi-Status"},
            {208, "Already Reported"},
            {226, "IM Used"},
            {300, "Multiple Choices"},
            {301, "Moved Permanently"},
            {302, "Found"},
            {303, "See Other"},
            {304, "Not Modified"},
            {307, "Temporary Redirect"},
            {308, "Permanent Redirect"},
            {400, "Bad Request"},
            {401, "Unauthorized"},
            {402, "Payment Required"},
            {403, "Forbidden"},
            {404, "Not Found"},
            {405, "Method Not Allowed"},
            {406, "Not Acceptable"},
            {407, "Proxy Authentication Required"},
            {408, "Request Timeout"},
            {409, "Conflict"},
            {410, "Gone"},
            {411, "Length Required"},
            {412, "Precondition Failed"},
            {413, "Content Too Large"},
            {414, "URI Too Long"},
            {415, "Unsupported Media Type"},
            {416, "Range Not Satisfiable"},
            {417, "Expectation Failed"},
            {418, "I'm a teapot"},
            {421, "Misdirected Request"},
            {422, "Unprocessable Entity"},
            {423, "Locked"},
            {424, "Failed Dependency"},
            {425, "Too Early"},
            {426, "Upgrade Required"},
            {428, "Precondition Required"},
            {429, "Too Many Requests"},
            {431, "Request Header Fields Too Large"},
            {451, "Unavailable For Legal Reasons"},
            {500, "Internal Server Error"},
            {501, "Not Implemented"},
            {502, "Bad Gateway"},
            {503, "Service Unavailable"},
            {504, "Gateway Timeout"},
            {505, "HTTP Version Not Supported"},
            {506, "Variant Also Negotiates"},
            {507, "Insufficient Storage"},
            {508, "Loop Detected"},
            {510, "Not Extended"},
            {511, "Network Authentication Required"}
        };
        auto it = _status_desc.find(statu);
        if(it == _status_desc.end()) return "Unknown";
        return it->second;
    }
//文件mime
    static std::string GetMime(const std::string& filename)
    {
        static std::unordered_map<std::string ,std::string> mimes{
            {".aac", "audio/aac"},
            {".abw", "application/x-abiword"},
            {".arc", "application/x-freearc"},
            {".avi", "video/x-msvideo"},
            {".azw", "application/vnd.amazon.ebook"},
            {".bin", "application/octet-stream"},
            {".bmp", "image/bmp"},
            {".bz",  "application/x-bzip"},
            {".bz2", "application/x-bzip2"},
            {".csh", "application/x-csh"},
            {".css", "text/css"},
            {".csv", "text/csv"},
            {".doc", "application/msword"},
            {".docx","application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
            {".eot", "application/vnd.ms-fontobject"},
            {".epub","application/epub+zip"},
            {".gif", "image/gif"},
            {".htm", "text/html"},
            {".html","text/html"},
            {".ico", "image/vnd.microsoft.icon"},
            {".ics", "text/calendar"},
            {".jar", "application/java-archive"},
            {".jpeg","image/jpeg"},
            {".jpg", "image/jpeg"},
            {".js",  "text/javascript"},
            {".json","application/json"},
            {".jsonld","application/ld+json"},
            {".mid", "audio/midi"},
            {".midi","audio/x-midi"},
            {".mjs", "text/javascript"},
            {".mp3", "audio/mpeg"},
            {".mpeg","video/mpeg"},
            {".mpkg","application/vnd.apple.installer+xml"},
            {".odp", "application/vnd.oasis.opendocument.presentation"},
            {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
            {".odt", "application/vnd.oasis.opendocument.text"},
            {".oga", "audio/ogg"},
            {".ogv", "video/ogg"},
            {".ogx", "application/ogg"},
            {".otf", "font/otf"},
            {".png", "image/png"},
            {".pdf", "application/pdf"},
            {".ppt", "application/vnd.ms-powerpoint"},
            {".pptx","application/vnd.openxmlformats-officedocument.presentationml.presentation"},
            {".rar", "application/x-rar-compressed"},
            {".rtf", "application/rtf"},
            {".sh",  "application/x-sh"},
            {".svg", "image/svg+xml"},
            {".swf", "application/x-shockwave-flash"},
            {".tar", "application/x-tar"},
            {".tif", "image/tiff"},
            {".tiff","image/tiff"},
            {".ttf", "font/ttf"},
            {".txt", "text/plain"},
            {".vsd", "application/vnd.visio"},
            {".wav", "audio/wav"},
            {".weba","audio/webm"},
            {".webm","video/webm"},
            {".webp","image/webp"},
            {".woff","font/woff"},
            {".woff2","font/woff2"},
            {".xhtml","application/xhtml+xml"},
            {".xls", "application/vnd.ms-excel"},
            {".xlsx","application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
            {".xml", "application/xml"},
            {".xul", "application/vnd.mozilla.xul+xml"},
            {".zip", "application/zip"},
            {".3gp", "video/3gpp"},
            {".3g2", "video/3gpp2"},
            {".7z",  "application/x-7z-compressed"}
        };
        size_t pos = filename.rfind('.'); //找后缀
        if(pos == std::string::npos) return "application/octet-stream";  //任何二进制类型
        std::string ext = filename.substr(pos);     //文件后缀
        auto it = mimes.find(ext);
        if(it == mimes.end()) return "application/octet-stream";  //任何二进制类型
        return it->second;
    } 
//判断是不是目录
    static bool IsDirectory(const std::string& path)
    {
        struct stat s;
        int ret = stat(path.c_str(),&s);
        if(ret < 0) return false;    //获取失败一般是由于路径无效导致的
        return S_ISDIR(s.st_mode);
    }
//判断是不是普通文件
    static bool IsRegular(const std::string& path)
    {
        struct stat s;
        int ret = stat(path.c_str(),&s);
        if(ret < 0) return false; 
        return S_ISREG(s.st_mode);
    }
//判断路径是否有效 是否会超出网站相对根目录
    static bool IsValid(const std::string& path)
    {
        std::vector<std::string> vs;
        int ret = Split(path,"/",&vs);
        int depth = 0;
        for(auto& s:vs)
        {
            if(s == "..") //上一层目录，那么深度减一
            {
                depth --;
                if(depth < 0)return false;
            }
            else if(s == "") //中间出现空串
            {
                return false;   //
            }
            else
            depth ++;
        }
        return true; 
    }
};

//保存http请求的各个要素
class HttpRequest   
{
public:
std::string _method; //请求方法
std::string _path;   //资源路径
std::unordered_map<std::string,std::string> _params;    //参数
std::string _version;   //协议版本
std::unordered_map<std::string,std::string> _headers;   //头部字段
std::string _body;      //正文
std::smatch _matches;   //请求行的正则匹配结果
public:
    HttpRequest():_version("HTTP/1.0"){}
    //添加参数
    void AddParam(const std::string& key , const std::string& val)
    {
        //如果已经存在该key，那么也直接更新
        _params[key] = val;
    }
    //判断是否有该参数
    bool HasParam(const std::string& key)const
    {
        auto it = _params.find(key);
        return it != _params.end();
    }
    //获取参数
    std::string GetParam(const std::string& key)const
    {
        auto it = _params.find(key);
        if(it == _params.end()) return "";  //返回空串
        return it->second;
    }
    //添加头部字段
    void AddHeader(const std::string& key , const std::string& val)
    {
        _headers[key]=val;
    }
    //判断是否有某个头部字段
    bool HasHeader(const std::string& key)const
    {
        auto it = _headers.find(key);
        return it != _headers.end();
    }
    //判断是不是短连接，如果是短连接那么返回一个响应之后就关闭连接了
    bool Close()const
    {
        //长短连接： Connection: close/keep-alive
        auto it = _headers.find("Connection");
        if(it == _headers.end() || it->second == "close") return true;
        return false;
    }
    void Reset()
    {
        _method.clear();
        _path.clear();
        _params.clear();
        _version.clear();
        _headers.clear();
        _body.clear();
        std::smatch tmp;
        _matches.swap(tmp); //由于smatch没有提供clear接口，所以我们使用swap来进行重置
        _body.clear();
    }
    //获取正文长度
    size_t ContentLength()const
    {
        auto it = _headers.find("Content-Length");
        if(it == _headers.end()) return 0;
        return std::stol(it->second);
    }
};

//http响应要素
class HttpResponse
{
public:
    int _statu; //响应状态码
    std::unordered_map<std::string,std::string> _headers;       //头部字段
    bool _redirect_flag ;   //重定向标志
    std::string _redirect_url;  //重定向的url
    std::string _body;      //响应正文
public:
    HttpResponse(int statu = 200):_statu(statu),_redirect_flag(false){} //默认构造,状态码默认为 200 "OK"
    void AddHeader(const std::string& key,const std::string& val)
    {
        _headers[key] = val;
    }
    bool HasHeader(const std::string& key) const
    {
        auto it = _headers.find(key);
        return it != _headers.end();
    }
    std::string GetHeader(const std::string& key) const
    {
        auto it = _headers.find(key);
        if(it == _headers.end()) return "";
        return it->second;
    }
    void SetContent(const std::string& body , const std::string& type = "application/octet-stream") //可以传mime，不传默认就是二进制流
    {
        _body = body;
        _headers["Content-Type"] = type;    //添加头部字段
    }
    void Reset()
    {
        _statu = 200;
        _headers.clear();
        _redirect_flag = false;
        _redirect_url.clear();
        _body.clear();
    }
    void SetRedirectUrl(const std::string& url , int statu = 302)  //"Found"  临时重定向的状态码
    {
        _redirect_url = url;
        _redirect_flag = true;
        _statu = statu;
    }
    //判断是不是短连接，如果是短连接那么返回一个响应之后就关闭连接了
    bool Close()const
    {
        //长短连接： Connection: close/keep-alive
        auto it = _headers.find("Connection");
        if(it == _headers.end() || it->second == "close") return true;
        return false;
    }
};

//处理状态
enum HttpRecvStatu{
    RECV_ERR,   //接收错误
    RECV_LINE,  //接收请求行
    RECV_HEAD,  //接收头部
    RECV_BODY,  //接收正文
    RECV_OVER   //接收完毕
};

//请求处理上下文
class HttpContext
{
public:
    HttpRecvStatu _recv_statu;  //处理进度
    int _resp_statu;            //响应状态码
    HttpRequest _req;           //请求
private:
//解析处理请求行
    void RecvLine(Buffer* buffer) 
    {
        if(_recv_statu != RECV_LINE) return;
        //1 获取请求行
        std::string line = buffer->GetLineAndPop();
        if(line == "")  //没有获取到一行,此时需要判断是数据不够还是因为数据接受错误了
        {
            if(buffer->ReadSize() > MAX_LINE_SIZE)  //大于8192
            {
                _recv_statu = RECV_ERR;                 //说明解析错误，报文在接收的时候有问题
                _resp_statu = 414;                      //url too long
            }   
            return;
        }
        //2 解析请求行
        return HandlerLine(line);
    }
    void HandlerLine(const std::string& line)
    {
        if(_recv_statu != RECV_LINE) return;
        //使用正则表达式进行解析
        std::smatch matches;
        std::regex e("(GET|HEAD|POST|PUT|DELETE) ([^?]*)(?:\\?(.*))? (HTTP/1\\.[01])(?:\n|\r\n)?");
        bool ret = std::regex_match(line,matches,e);
        if(ret == false)        //说明请求出错
        {
            _recv_statu = RECV_ERR;
            _resp_statu = 400;      //Bad Request
            return;
        }
        //走到这里说明正则匹配成功,而smacth是重载了方括号运算符的，我们可以直接使用
        _req._method = matches[1];
        //这里我们需要对方法进行处理，将其转换成大写，因为可能会有不标准的请求将方法写成小写。
        std::transform(_req._method.begin(),_req._method.end(),_req._method.begin(),::toupper);
        //前两个参数表示转换的数据的范围，第三个参数表示转换之后的目的地址，第四个参数表示转换的方法，使用C库的全局的toupper函数来转大写
        _req._path = Util::UrlDecode(matches[2],false);     //url需要进行解码，不需要+转空格
        std::string params = Util::UrlDecode(matches[3],true); //参数需要及逆行解码，需要+转空格
        //然后就是将参数解析为kv的格式
        std::vector<std::string> arr;
        Util::Split(params,"&",&arr);   //  参数以param进行分割
        for(auto&s:arr) //然后逐个提取每一个kv式的参数
        {  
            std::vector<std::string> kv;
            int ret = Util::Split(s,"=",&kv);
            if(ret != 2) //如果不是一个kv，那么就报错
            {
                _recv_statu = RECV_ERR;
                _resp_statu = 400;  //Bad Request
                return;
            }
            //提取出来就放到参数的 map 中
            _req.AddParam(kv[0],kv[1]);
        }
        //最后就是提取版本号
        _req._version = matches[4];
    }   

//获取解析头部字段
    void RecvHeader(Buffer* buffer)
    {
        if(_recv_statu != RECV_HEAD) return;
        //提取每一行
        while(1)
        {
            //1 获取头部字段
            std::string line = buffer->GetLineAndPop();
            if(line == "")  //没有获取到一行,此时需要判断是数据不够还是因为数据接受错误了
            {
                if(buffer->ReadSize() > MAX_LINE_SIZE)  //大于8192
                {
                    _recv_statu = RECV_ERR;                 //说明解析错误，报文在接收的时候有问题
                    _resp_statu = 414;                      //url too long
                }   
                return;
            }
            //2 解析头部字段
            if(line == "\r\n" || line == "\n")
            {
                //头部字段解析完了
                _recv_statu = RECV_BODY;
                return;
            }
            //否则解析这一行
            bool ret = HandlerHeader(line);
            if(!ret) return;    //因为头部字段可能会有问题,需要使用一个返回值来判别
        }
    }
    bool HandlerHeader(std::string& line)
    {
        if(_recv_statu != RECV_HEAD) return false; 
        //先去掉回车和换行
        if(line.back() == '\n') line.pop_back();
        if(line.back() == '\r') line.pop_back();
        std::vector<std::string> kv;
        int ret = Util::Split(line,": ",&kv);
        if(ret != 2) 
        {
            _recv_statu = RECV_ERR;
            _resp_statu = 400;      //Bad Request
            return false;
        }
        _req.AddHeader(kv[0],kv[1]);
        return true;
    }
//获取正文
    void RecvBody(Buffer* buffer)
    {
        if(_recv_statu != RECV_BODY) return;
        int len = _req.ContentLength();
        if(len ==0) 
        {
            _recv_statu = RECV_OVER; //没有正文，直接返回
            return;
        }
        int size = len - _req._body.size(); //还需要接收的正文的长度
        if(size > buffer->ReadSize()) //说明不够，能读多少就读多少
        {
            _req._body.append(buffer->ReadPosition(),buffer->ReadSize());
            buffer->MoveReadOffset(buffer->ReadSize());
            return;
        }
        //走到这里说明能读完当前请求的正文
        _req._body.append(buffer->ReadPosition(),size);
        buffer->MoveReadOffset(size);
        _recv_statu = RECV_OVER;
    }
public:
    HttpContext():_recv_statu(RECV_LINE),_resp_statu(200){}
    void Reset()
    {
        _recv_statu = RECV_LINE;
        _resp_statu = 200;
        _req.Reset();
    }
    int RespStatu()const {return _resp_statu;}
    HttpRecvStatu RecvStatu()const {return _recv_statu;}
    HttpRequest& GetRequest() {return _req;}
    void RecvHttpRequest(Buffer* buffer)
    {
        switch(_recv_statu)
        {
            case RECV_LINE: RecvLine(buffer);   //不需要break，因为可能提取完请求行之后还有后续的内容可以提取,就算请求行没提取完，每一个提取的函数前面有一个判断，可以直接返回
            case RECV_HEAD: RecvHeader(buffer);
            case RECV_BODY: RecvBody(buffer);
        }
    }

};

//支持Http协议的服务器
class HttpServer
{
private:
    TcpServer _server;
    std::string _base_path; //网页根目录
    //五张路由表
    using Handler = std::function<void(const HttpRequest&,HttpResponse*)>; 
    using HandlerTable = std::vector<std::pair<std::regex,Handler>>;
    HandlerTable _get_route; 
    HandlerTable _post_route; 
    HandlerTable _head_route; 
    HandlerTable _put_route; 
    HandlerTable _delete_route;
private:
    void OnConnect(const PtrConnection& conn)
    {
        //设置一个上下文
        HttpContext ctx;
        conn->SetContext(ctx);
    }
    void OnMessage(const PtrConnection& conn,Buffer* buf)   //获取新数据回调
    {
        while(buf->ReadSize() > 0)      //从逻辑上来说 while(1) 也是一样的
        {
            // 1 获取上下文
            Any* context = conn->GetContext();
            HttpContext* pctx = context->GetData<HttpContext>();
            // 2 解析缓冲区数据
            pctx->RecvHttpRequest(buf);
            HttpRequest& req = pctx->GetRequest();
            HttpResponse resp;
            //判断解析是否出错
            if(pctx->RespStatu() >= 400)  //请求解析出错,此时的_recv_statu 也一定是RECV_ERR
            {
                HandlerError(req,resp,pctx->RespStatu());         //调用错误处理方法
                WriteResponse(conn,req,resp);   //返回响应
                conn->ShutDown();               //发生错误就关闭连接
                return;
            }
            if(pctx->RecvStatu() != RECV_OVER)      //还没收到一个完整请求
            return;
            //走到这里说明req是一个完整的请求
            // 3 数据处理，路由
            Route(req,resp);    //进行方法路由，判断是不是静态资源请求。
            // 4 返回给客户端
            WriteResponse(conn,req,resp);
            // 5 处理完之后重置上下文
            pctx->Reset();
            // 6 判断长短连接
            if(resp.Close())    //如果是短连接就直接关闭
            {
                conn->ShutDown();
                return;
            }
            //如果是长连接就需要搞成循环，读取下一个报文
        }
    }
    void HandlerError(HttpRequest& req , HttpResponse& resp ,int statu)
    {
        std::string body;
        body += "<!DOCTYPE html>";
        body += "<html><head><title>";
        body += std::to_string(statu);
        body += Util::StatuDesc(statu);
        body += "</title></head><body><h1>抱歉，该页面无法找到。</h1>";
        body += "<p>请检查您输入的网址是否正确，或者 <a href=\"/\">返回首页</a>。</p>";
        body += "</body></html>";
        resp._body = body;
        resp.AddHeader("Content-Type","text/html");
    }
    void WriteResponse(const PtrConnection& conn , HttpRequest& req , HttpResponse& resp)
    {
        // 1 先把响应的头部字段完善了
        if(req.Close()) resp.AddHeader("Connection","close");
        else  resp.AddHeader("Connection","keep-alive");
        if(resp._body.size()&&!resp.HasHeader("Content-Length")) resp.AddHeader("Content-Length",std::to_string(resp._body.size()));
        if(resp._body.size() && !resp.HasHeader("Content-Type")) resp.AddHeader("Content-Type","application/octet-stream");
        //重定向信息
        if(resp._redirect_flag) resp.AddHeader("Location",resp._redirect_url);
        // 2 组织响应
        std::ostringstream out;
        //响应行  HTTP/1.0 404 NotFound\r\n
        out<<req._version<<" "<<std::to_string(resp._statu)<<" "<<Util::StatuDesc(resp._statu)<<"\r\n";
        //头部字段
        for(auto& p : resp._headers)
        {
            out<<p.first<<": "<<p.second<<"\r\n";
        } 
        //空行
        out<<"\r\n";
        //正文
        out<<resp._body;
        // 3 发送
        conn->Send(out.str().c_str(),out.str().size());
    }
    void Route(HttpRequest& req , HttpResponse& resp)
    {
        if(IsFileResquest(req,resp))       //处理静态资源的方法，如果不是静态资源会返回false
        return HandlerFile(req,resp);
        //否则就需要到几个方法表中进行路由
        if(req._method == "GET")
        return Dispatcher(req,resp,_get_route);
        if(req._method == "POST")
        return Dispatcher(req,resp,_post_route);
        if(req._method == "PUT")
        return Dispatcher(req,resp,_put_route); 
        if(req._method == "HEAD")
        return Dispatcher(req,resp,_head_route);
        if(req._method == "DELETE")
        return Dispatcher(req,resp,_delete_route);         
        //如果走到了这里，说明前面的处理方法都不行，那么一定是请求出问题了
        resp._statu = 405;      //Method Not Allowed
    }
    bool IsFileResquest(HttpRequest& req , HttpResponse& resp) //判断以及处理静态资源
    {
        // 1 首先需要判断有没有设置资源根目录
        if(_base_path.empty()) return false;    //肯定不是静态资源请求
        // 2 静态资源请求的方法必须是 GET 或者 HEAD ，因为其他的方法不是用来获取资源的
        if(!(req._method == "GET" || req._method == "HEAD")) return false;
        //然后静态资源请求的路径必须是一个合法的路径
        if(Util::IsValid(req._path) == false) return false;
        //最后就需要判断请求的路径的资源是否存在
        //但是我们需要考虑路径是目录的时候，给它加上一个 index.html
        std::string path = req._path;
        if(path.back() == '/') path += "index.html";
        //判断文件是否存在
        if(Util::IsRegular(path) == false) return false;
        return true;    //走到这里才算是一个静态资源请求
    }
    void HandlerFile(HttpRequest& req , HttpResponse& resp) //处理静态资源请求
    {
        std::string path = req._path;
        if(path.back() == '/') path +="index.html";
        Util::ReadFile(path,&resp._body);
        //然后设置响应头部字段
        //在这里我们可以只设置 Content-Type 字段，Content-Length可以交给WriteResponse接口来设置
        std::string mime = Util::GetMime(path);
        resp.AddHeader("Content-Type",mime);
    }
    void Dispatcher(HttpRequest& req , HttpResponse& resp , const HandlerTable& table)
    {
        for(std::pair<const std::regex& , Handler> p: table)
        {
            const std::regex& e = p.first;
            const Handler& cb = p.second;
            std::smatch matches;
            bool ret = std::regex_match(req._path,matches,e);
            if(ret) return cb(req,&resp);
        }
        //走到这里说明路由表中没有对应的方法
        resp._statu = 404;  //Not Found
    }
public:
    HttpServer(int port ,int delay = 30):_server(port) 
    {
        _server.EnableInactiveRelease(delay);   //我们的http服务器默认是开启超时释放的
        _server.SetConnectCallBack(std::bind(&HttpServer::OnConnect,this,std::placeholders::_1));
        _server.SetMessageCallBack(std::bind(&HttpServer::OnMessage,this,std::placeholders::_1,std::placeholders::_2));
    }
    void SetBasePath(const std::string& basedir)
    {
        _base_path = basedir;
    }
    void Get(const std::regex& e , const Handler& cb)  //设置GET
    {
        _get_route.push_back(std::make_pair(e,cb));
    }
    void Post(const std::regex& e , const Handler& cb)  //设置POST
    {
        _post_route.push_back(std::make_pair(e,cb));
    }
    void Put(const std::regex& e , const Handler& cb)  //设置PUT
    {
        _put_route.push_back(std::make_pair(e,cb));
    } 
    void Head(const std::regex& e , const Handler& cb)  //设置HEAD
    {
        _head_route.push_back(std::make_pair(e,cb));
    }
    void Delete(const std::regex& e , const Handler& cb)  //设置DELETE
    {
        _delete_route.push_back(std::make_pair(e,cb));
    }        
    void EnableInactiveRelease(int delay = 30) //启动非活跃销毁   
    {
        _server.EnableInactiveRelease(delay);
    }
    void SetThreadCount(int cnt)    //设置线程数量
    {
        _server.SetThreadCount(cnt);
    }
    void Start()                    //启动服务器
    {
        _server.Start();
    }
};

#endif